Andy Armstrong contributes support for Wintec WBT-200.
authorrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Mon, 19 Jun 2006 14:42:20 +0000 (14:42 +0000)
committerrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Mon, 19 Jun 2006 14:42:20 +0000 (14:42 +0000)
git-svn-id: http://gpsbabel.googlecode.com/svn/trunk@2163 f51c46e8-681c-474f-0cfe-069cfd0219fb

gpsbabel/Makefile.in
gpsbabel/msvc/GPSBabel.dsp
gpsbabel/msvc/GPSBabel.vcproj
gpsbabel/msvc/build.bat
gpsbabel/vecs.c
gpsbabel/wbt-200.c [new file with mode: 0644]
gpsbabel/xmldoc/formats/options/wbt-erase.xml [new file with mode: 0644]
gpsbabel/xmldoc/formats/wbt.xml [new file with mode: 0644]

index dc2cca00eb1951b4955be6000088fa5a5a8b4542..13829c549faee12990cc79e9026554d23655f020 100644 (file)
@@ -44,7 +44,8 @@ FMTS=magproto.o gpx.o geo.o mapsend.o mapsource.o garmin_tables.o \
        vcf.o overlay.o kml.o google.o lowranceusr.o an1.o tomtom.o \
        tef_xml.o maggeo.o pathaway.o vitosmt.o gdb.o bcr.o coto.o \
        ignrando.o stmwpp.o msroute.o cst.o nmn4.o mag_pdb.o compegps.o \
-       yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o
+       yahoo.o unicsv.o wfff_xml.o garmin_txt.o axim_gpb.o gpssim.o \
+       wbt-200.o
 
 FILTERS=position.o duplicate.o arcdist.o polygon.o smplrout.o \
        reverse_route.o sort.o stackfilter.o trackfilter.o discard.o \
index 43b25bd20d40396657d383f136d48c68664841b8..c1fcba872128ec0740944a5d81eba42653a74a92 100644 (file)
@@ -315,6 +315,10 @@ SOURCE=..\brauniger_iq.c
 # End Source File\r
 # Begin Source File\r
 \r
+SOURCE=..\wbt-200.c\r
+# End Source File\r
+# Begin Source File\r
+\r
 SOURCE=..\cetus.c\r
 # End Source File\r
 # Begin Source File\r
index 0b91ed518a6216564f4c34343fa6e6f764bcf76c..e233aae5bf55c9d1ab6aaefc5035d7be7e61f6a6 100644 (file)
             <File
                                RelativePath="..\brauniger_iq.c">
                        </File>
+            <File
+                               RelativePath="..\wbt-200.c">
+                       </File>
                        <File
                 RelativePath="..\cet.c">
             </File>
index 06d5d1fad75c7f3763c326db62b796bb98288feb..26c752aa680be64a0eb73a13027ff65468d5f815 100644 (file)
@@ -2,7 +2,7 @@ setlocal
 set include=%include%;expat;..\coldsync;c:\tools\c
 set SOURCEJEEPS=..\jeeps\gpsapp.c ..\jeeps\gpscom.c ..\jeeps\gpsmath.c ..\jeeps\gpsmem.c ..\jeeps\gpsprot.c ..\jeeps\gpsread.c ..\jeeps\gpsrqst.c ..\jeeps\gpssend.c ..\jeeps\gpsserial.c ..\jeeps\gpsusbread.c ..\jeeps\gpsusbsend.c ..\jeeps\gpsusbstub.c ..\jeeps\gpsusbwin.c ..\jeeps\gpsutil.c
 set SOURCEMAG=..\maggeo.c ..\magnav.c ..\magproto.c
-set SOURCE=..\xmltag.c ..\strptime.c ..\trackfilter.c ..\gdb.c ..\bcr.c ..\discard.c ..\formspec.c ..\an1.c ..\arcdist.c ..\brauniger_iq.c ..\cetus.c ..\coastexp.c ..\coldsync\pdb.c ..\copilot.c ..\csv_util.c ..\delgpl.c ..\duplicate.c ..\easygps.c ..\filter_vecs.c ..\garmin.c ..\garmin_tables.c ..\gcdb.c ..\geo.c ..\geoniche.c ..\glogbook.c ..\google.c ..\gpilots.c ..\gpspilot.c ..\gpx.c ..\grtcirc.c ..\hiketech.c ..\holux.c ..\hsa_ndv.c ..\html.c ..\igc.c ..\internal_styles.c  ..\kml.c ..\lowranceusr.c ..\main.c ..\mapopolis.c ..\mapsend.c ..\mapsource.c ..\mkshort.c ..\navicache.c ..\netstumbler.c ..\nmea.c ..\overlay.c ..\ozi.c ..\palmdoc.c ..\pathaway.c ..\pcx.c ..\polygon.c ..\position.c ..\psitrex.c ..\psp.c ..\queue.c ..\quovadis.c ..\reverse_route.c ..\route.c ..\saroute.c ..\shape.c ..\shapelib\dbfopen.c ..\shapelib\shpopen.c ..\smplrout.c ..\sort.c ..\stackfilter.c ..\tef_xml.c ..\text.c ..\tiger.c ..\tmpro.c ..\tomtom.c ..\tpg.c ..\util.c ..\util_crc.c ..\uuid.c ..\vcf.c ..\vecs.c ..\vitosmt.c ..\vmem.c ..\waypt.c ..\xcsv.c ..\xmlgeneric.c ..\fatal.c ..\globals.c ..\cet_util.c ..\cet.c ..\nmn5.c ..\nmn4.c ..\cst.c ..\msroute.c ..\stmwpp.c ..\ignrando.c ..\tpo.c
+set SOURCE=..\xmltag.c ..\strptime.c ..\trackfilter.c ..\gdb.c ..\bcr.c ..\discard.c ..\formspec.c ..\an1.c ..\arcdist.c ..\brauniger_iq.c ..\wbt-200.c ..\cetus.c ..\coastexp.c ..\coldsync\pdb.c ..\copilot.c ..\csv_util.c ..\delgpl.c ..\duplicate.c ..\easygps.c ..\filter_vecs.c ..\garmin.c ..\garmin_tables.c ..\gcdb.c ..\geo.c ..\geoniche.c ..\glogbook.c ..\google.c ..\gpilots.c ..\gpspilot.c ..\gpx.c ..\grtcirc.c ..\hiketech.c ..\holux.c ..\hsa_ndv.c ..\html.c ..\igc.c ..\internal_styles.c  ..\kml.c ..\lowranceusr.c ..\main.c ..\mapopolis.c ..\mapsend.c ..\mapsource.c ..\mkshort.c ..\navicache.c ..\netstumbler.c ..\nmea.c ..\overlay.c ..\ozi.c ..\palmdoc.c ..\pathaway.c ..\pcx.c ..\polygon.c ..\position.c ..\psitrex.c ..\psp.c ..\queue.c ..\quovadis.c ..\reverse_route.c ..\route.c ..\saroute.c ..\shape.c ..\shapelib\dbfopen.c ..\shapelib\shpopen.c ..\smplrout.c ..\sort.c ..\stackfilter.c ..\tef_xml.c ..\text.c ..\tiger.c ..\tmpro.c ..\tomtom.c ..\tpg.c ..\util.c ..\util_crc.c ..\uuid.c ..\vcf.c ..\vecs.c ..\vitosmt.c ..\vmem.c ..\waypt.c ..\xcsv.c ..\xmlgeneric.c ..\fatal.c ..\globals.c ..\cet_util.c ..\cet.c ..\nmn5.c ..\nmn4.c ..\cst.c ..\msroute.c ..\stmwpp.c ..\ignrando.c ..\tpo.c
 cl /c ..\coldsync\util.c -Focoldsyncutil.obj
 cl /c ..\gpsutil.c -Fogpsutil2.obj
 cl /Fegpsbabel.exe %source% %sourcejeeps% %sourcemag% coldsyncutil.obj gpsutil2.obj -DVERSION=\"1\" -D__WIN32__ -DWIN32_LEAN_AND_MEAN -DNO_USB Expat\libexpat.lib
index 02c4bb0ef0c1b6bed441aa7f129644ae3fccd18c..d7f2e0eb50ec0e8be1b88993c78fcab66de15a09 100644 (file)
@@ -106,6 +106,7 @@ extern ff_vecs_t vitosmt_vecs;
 extern ff_vecs_t wfff_xml_vecs;
 extern ff_vecs_t xcsv_vecs;
 extern ff_vecs_t yahoo_vecs;
+extern ff_vecs_t wbt_vecs;
 
 static
 vecs_t vec_list[] = {
@@ -380,6 +381,12 @@ vecs_t vec_list[] = {
                 "Brauniger IQ Series Barograph Download",
                 NULL
         },
+        {
+                &wbt_vecs,
+                "wbt",
+                "Wintec WBT-100/200 GPS Download",
+                NULL
+        },
         {
                 &hiketech_vecs,
                 "hiketech",
diff --git a/gpsbabel/wbt-200.c b/gpsbabel/wbt-200.c
new file mode 100644 (file)
index 0000000..74a56bb
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Serial download of track data from a Wintec WBT-200.
+ * 
+ * Copyright (C) 2006 Andy Armstrong
+ * 
+ * This program is free software; you can redistribute it and/or modify it 
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License along 
+ * with this program; if not, write to the Free Software Foundation, Inc., 
+ * 59 Temple Place - Suite 330, Boston, MA 02111 USA 
+ */
+
+#include "defs.h"
+#include "jeeps/gpsserial.h"
+#include "grtcirc.h"
+#include <errno.h>
+
+/*
+    A conversation looks like this
+
+    >> $PFST,FIRMWAREVERSION
+    << $PFST,FIRMWAREVERSION,WBT200,3,31,6090,R2*77
+    >> $PFST,NORMAL
+    << $PFST,NORMAL,*02
+    >> $PFST,READLOGGER
+    << $PFST,READLOGGER,*17
+    << 0xFFFF, <length>, 0xFFFF
+    << (length + 1) * 12 bytes of data
+    << ====
+    >> $PFST,NORMAL
+    << $PFST,NORMAL,*02
+*/
+
+static gpsdevh *fd;
+static char *port;
+static char *erase;
+
+#define MYNAME      "WBT-100/200"
+#define PRESTRKNAME "PRESALTTRK"
+#define NL          "\x0D\x0A"
+
+/* Number of lines to skip while waiting for an ACK from a command. I've seen
+ * conversations with up to 30 lines of cruft before the response so 50 isn't
+ * too crazy.
+ */
+#define RETRIES     50
+
+static void db(int l, const char *msg, ...) {
+    va_list ap;
+    va_start(ap, msg);
+    if (global_opts.debug_level >= l) {
+        vprintf(msg, ap);
+    }
+    va_end(ap);
+}
+
+/* Read a single character from the serial port. Kind of gross but we do
+ * it like this so we can use Jeeps (which doesn't have an equivalent
+ * function). Returns -1 if no char is available, -2 on error or the
+ * retrieved char.
+ */
+static int rd_char() {
+    if (GPS_Serial_Chars_Ready(fd)) {
+        unsigned char c;
+        if (GPS_Serial_Read(fd, &c, 1) != 1) {
+            return -2;
+        } else {
+            return c;
+        }
+    } else {
+        return -1;
+    }
+}
+
+/* Blocking version of above. It would be nicer to use select on the fd
+ * rather than spinning in a loop here - but we're trying to stay platform
+ * independent.
+ */
+static int rd_char_b() {
+    int c = rd_char();
+    while (c == -1) {
+        c = rd_char();
+    }
+    
+    return c;
+}
+
+/* Swallow any pending output from GPS */
+
+static int rd_drain() {
+    int c = rd_char();
+    while (c >= 0) {
+        c = rd_char();
+    }
+    
+    return c == -1 ? 0 : c;
+}
+
+/* Read a line (up to 0x0A). Carriage returns are filtered out. Always tries to
+ * read an entire line but discards any characters beyond len (because we're
+ * only ever interested in fairly short lines.
+ *
+ * Returns the number of characters read or -2 on error. The buffer will contain
+ * the (possibly truncated) string without any line terminator characters. The
+ * buffer will always be null terminated.
+ */
+static int rd_line(char *buf, int len) {
+    int c, pos = 0, nr = 0;
+    c = rd_char_b();
+    while (c >= 0 && c != 0x0A) {
+        nr++;
+        if (c != 0x0D && pos < len-1) {
+            buf[pos++] = (unsigned char) c;
+        }
+        c = rd_char_b();
+    }
+    
+    buf[pos] = '\0';
+    
+    return c < 0 ? c : nr;
+}
+
+static int wr_cmd(const char *cmd) {
+    return GPS_Serial_Write(fd, cmd, strlen(cmd));
+}
+
+static void rd_init(const char *fname) {
+    port = xstrdup(fname);
+
+    db(1, "Opening port...\n");
+    if (!GPS_Serial_On(port, &fd)) {
+        fatal(MYNAME ": Can't initialise port '%s'\n", port);
+    }
+}
+
+static void rd_deinit(void) {
+    db(1, "Closing port...\n");
+    if (!GPS_Serial_Off(fd)) {
+        fatal(MYNAME ": Can't shut down port '%s'\n", port);
+    }
+
+    xfree(port);
+}
+
+static int starts_with(const char *buf, const char *pat) {
+    return memcmp(buf, pat, strlen(pat)) == 0;
+}
+
+/* Send a command then wait for a line starting with the command string
+ * to be returned.
+ */
+static void do_cmd(const char *cmd, const char *expect, char *buf, int len) {
+    int rc, try;
+
+    if (rd_drain() < 0) {
+        fatal(MYNAME ": Read error\n");
+    }
+
+    wr_cmd(cmd); 
+    wr_cmd(NL);
+
+    db(2, "Cmd: %s\n", cmd);
+
+    /* We may need to skip a load of data to start with - the unit streams
+     * NMEA data all the time so it's highly likely that it'll be in the
+     * middle of an NMEA sentence when we start listening.
+     */
+    for (try = 0; try < RETRIES; try++) {
+        if (rc = rd_line(buf, len), rc < 0) {
+            fatal(MYNAME ": Read error\n");
+        }
+        if (starts_with(buf, expect)) {
+            db(2, "Got: %s\n", buf);
+            return;
+        }
+        db(2, "Skip %d: %s\n", try, buf);
+    }
+
+    fatal(MYNAME ": Bad response from unit\n");
+}
+
+static void do_simple(const char *cmd, char *buf, int len) {
+    do_cmd(cmd, cmd, buf, len);
+}
+
+static void hd(const void *d, int len) {
+    const unsigned char *dd = d;
+    while (len-- > 0) {
+        db(3, "%02x ", *dd++);
+    }
+    db(3, "\n");
+}
+
+static void rd_buf(void *buf, int len) {
+    char *bp = buf;
+    
+    while (len > 0) {
+        int rc = GPS_Serial_Read(fd, bp, len);
+        if (rc < 0) {
+            fatal(MYNAME ": Read error\n");
+        }
+        len -= rc;
+        bp  += rc;
+    }
+}
+
+static void data_read(void) {
+    /* Awooga! Awooga! Statically allocated buffer danger!
+     * Actually, it's OK because rd_line can read arbitrarily
+     * long lines returning only the first N characters
+     */
+    char        line_buf[100];
+    int         count, d;
+    gbuint32    tim, ptim;
+    double      lat, lon;
+    double      plat, plon;     /* previous point */
+    struct tm   t;
+    time_t      rtim;
+       route_head      *route_head = NULL; 
+       waypoint        *wpt        = NULL;
+
+    do_simple("$PFST,FIRMWAREVERSION", line_buf, sizeof(line_buf));
+    do_simple("$PFST,NORMAL",          line_buf, sizeof(line_buf));
+    do_simple("$PFST,READLOGGER",      line_buf, sizeof(line_buf));
+    
+    /* Now we're into binary mode */
+    rd_buf(line_buf, 6);            /* six byte header */
+    count = le_read16(line_buf + 2) + 1;
+    if (count == 0x10000) {
+        count = 0;
+    }
+    db(1, "Reading %d data\n", count);
+    for (d = 0; d < count; d++) {
+        rd_buf(line_buf, 12);       /* twelve byte record */
+        tim = le_read32(line_buf + 0);
+        
+        lat = (double) ((gbint32) le_read32(line_buf + 4)) / 10000000;
+        lon = (double) ((gbint32) le_read32(line_buf + 8)) / 10000000;
+        
+        t.tm_sec    = ((tim >>  0) & 0x3F);
+        t.tm_min    = ((tim >>  6) & 0x3F);
+        t.tm_hour   = ((tim >> 12) & 0x1F);
+        t.tm_mday   = ((tim >> 17) & 0x1F);
+        t.tm_mon    = ((tim >> 22) & 0x0F) - 1;
+        t.tm_year   = ((tim >> 26) & 0x3F) + 100;
+        
+        rtim = mkgmtime(&t);
+
+        if (lat >= 100) {
+            /* Start new track */
+            lat -= 100;
+            route_head = NULL;
+        } else {
+               wpt = waypt_new();
+               
+               wpt->latitude       = lat;;
+               wpt->longitude      = lon;
+               wpt->altitude       = 0.0;
+               wpt->creation_time  = rtim;
+               wpt->centiseconds   = 0;
+               
+               /* OK to reuse buffer now */
+               sprintf(line_buf, "WP%04d", d + 1);
+               wpt->shortname      = xstrdup(line_buf);
+               
+                       wpt->speed          = radtometers(
+                                           gcdist(RAD(plat), RAD(plon), 
+                                                  RAD(lat),  RAD(lon))) /
+                                             (rtim - ptim);
+                       wpt->course         = DEG(heading(RAD(plat), RAD(plon),
+                                                     RAD(lat),  RAD(lon)));
+               wpt->pdop               = 0;
+               wpt->fix                    = fix_unknown;
+               wpt->sat            = 0;
+
+               if (NULL == route_head) {
+                   db(1, "New Track\n");
+                       route_head = route_head_alloc();
+                       track_add_head(route_head);
+               }
+
+               track_add_wpt(route_head, wpt);
+        }
+               
+               ptim = rtim;
+               plat = lat;
+               plon = lon;
+    }
+    
+    /* Erase data? */
+    
+    if (*erase != '0') {
+        int f;
+        db(1, "Erasing data\n");
+        for (f = 27; f <= 31; f++) {
+            sprintf(line_buf, "$PFST,REMOVEFILE,%d", f);
+            do_cmd(line_buf, "$PFST,REMOVEFILE", line_buf, sizeof(line_buf));
+        }
+        db(1, "Reclaiming free space\n");
+        for (f = 0; f <= 3; f++) {
+            sprintf(line_buf, "$PFST,FFSRECLAIM,%d", f);
+            do_cmd(line_buf, "$PFST,FFSRECLAIM", line_buf, sizeof(line_buf));
+        }
+    }
+
+    do_simple("$PFST,NORMAL", line_buf, sizeof(line_buf));
+    
+}
+
+static arglist_t wbt_args[] = {
+    { "erase", &erase, "Erase device data after download", 
+        "0", ARGTYPE_BOOL, ARG_NOMINMAX },
+    ARG_TERMINATOR
+};
+
+ff_vecs_t wbt_vecs = {
+    ff_type_serial,
+    { ff_cap_none, ff_cap_read, ff_cap_none },
+    rd_init,
+    NULL,
+    rd_deinit,
+    NULL,
+    data_read,
+    NULL,
+    NULL, 
+    wbt_args,
+    CET_CHARSET_UTF8, 1         /* master process: don't convert anything | CET-REVIEW */
+};
diff --git a/gpsbabel/xmldoc/formats/options/wbt-erase.xml b/gpsbabel/xmldoc/formats/options/wbt-erase.xml
new file mode 100644 (file)
index 0000000..71aaf2c
--- /dev/null
@@ -0,0 +1 @@
+<para>This option erases the track log from the device after download.</para>
\ No newline at end of file
diff --git a/gpsbabel/xmldoc/formats/wbt.xml b/gpsbabel/xmldoc/formats/wbt.xml
new file mode 100644 (file)
index 0000000..215f0fe
--- /dev/null
@@ -0,0 +1,9 @@
+<para>Serial download protocol for the <productname>Wintec WBT-200</productname> GPS data logger. Although untested it is expected that this will also support the WBT-100.</para>
+<para>
+<ulink url="http://www.semsons.com/wi3mugpsrebt.html">Wintec WBT-200</ulink>
+</para>
+<example id="wbt-on-macos">
+  <title>Command showing WBT-200 download and erase over Bluetooth on Mac OS X</title>
+  <screen format="linespecific">gpsbabel -i wbt,erase -f /dev/cu.WBT200-SPPslave-1 -o gpx -F out.gpx</screen>
+</example>
+